using System;
using System.Collections;
using System.Data.SqlClient;
using System.Web.Services;
using System.Reflection;
using System.Security;
using System.Xml;

/*	-----------------------------------------------------------------------	
	Copyright:	umlungu consulting (pty) ltd
	Author:		Alan Benington	
	Started:	2005-09-08
	Status:		release
	Version:	2.0.0
	Build:		20070501
	License:	GNU General Public License
	-----------------------------------------------------------------------	*/
	
/*	-----------------------------------------------------------------------	
	Development Notes:
	==================
	20070124:	This is a minimal implementation of a class to provide some of the 
				functionality of x_status, ie: provide an xml wrapper to return status
				for procedural calls (eg web services).
				This class is not derived from System.Web.Services.WebService and would 
				typically be used as to create utility objects within methods or other
				classes.
	20070501:	Extend this class so that it can be used as a base for classes that
				need to have the basis of an XmlDocument for eg to 'publish' the class
				within a webservice. This is done by the following:
				- Ability to add a parent element (similar to the x_result xml structure)
	20071102:	Add the 'output' type of structure (see iProvision)
	-----------------------------------------------------------------------	*/
	
	//[assembly:AssemblyKeyFileAttribute(@"engineX.snk")]
	
namespace umlungu.engineX {
	/// <summary>Description of the enumeration</summary>
	public enum x_statusType {
		/// <summary>Default type of status, ie simple xml structure</summary>
	    def = 0,
		/// <summary>Result type of status, ie xml structure within result object element</summary>
	    result = 1,
		/// <summary>Result type of output, ie 'output' xml structure</summary>
		output = 2,
		/// <summary>Another type of status (not defined)</summary>
	    other = 3
	}
	/// <summary>
	/// umlungu EngineX class. 
	/// class for the EngineX status xml.
	/// </summary>
	public class x_status : System.Xml.XmlDocument {
		#region Invisible properties
		private const string DEFAULT_OK = "Ok";
		private const string DEFAULT_ERROR_CODE = "NotOk";
		private const string DEFAULT_ERROR = "Undefined error";
		private const string RESULT_XML = "<{0}><Status><Code>0</Code><Description/></Status></{0}>";
		private const string RESULT_NODEXML = "<{0}><Status><Code>0</Code><Description/>{1}</Status></{0}>";
		private const string STATUS_XML = "<Status><Code>0</Code><Description/>{0}</Status>";
		private const string OUTPUT_XML = "<Status><output><code>0</code><message/>{0}</output></Status>";

		private const string SELECT_STATUS = "//Status";
		private const string SELECT_OUTPUT = "//Status/output";
		private const string SELECT_CODE = "//Status/Code";
		private const string SELECT_DESC = "//Status/Description";
		private const string SELECT_CDE = "//output/code";
		private const string SELECT_MSG = "//output/message";
		#endregion

		#region Visible properties
		private string rootName = "";
		/// <summary>Definiton of the name</summary>
		/// <value>Description of the value of the name</value>
		public string RootName {
			get { return rootName; }
			set { rootName = value; }
		}
		private x_statusType type = x_statusType.def;
		/// <summary>The type of the status (ie the xml format)</summary>
		/// <value>An x_statusType to define the type of xml format of the status object</value>
		public x_statusType Type {
			get { return type; }
			set { type = value; }
		}
		/// <summary>Start timing mark</summary>
		/// <value>Date-Time of execution start</value>
		protected DateTime starttime;
		/// <summary>The status decription</summary>
		/// <value>String describing the status</value>
		public string Description {
			get { return DocumentElement.SelectSingleNode((type == x_statusType.output)? SELECT_MSG : SELECT_DESC).InnerText; }
			set { DocumentElement.SelectSingleNode((type == x_statusType.output)? SELECT_MSG : SELECT_DESC).InnerText = value; }
		}
		/// <summary>The status code</summary>
		/// <value>String containing the status code</value>
		public string Code {
			get { return DocumentElement.SelectSingleNode((type == x_statusType.output)? SELECT_CDE : SELECT_CODE).InnerText; }
			set { DocumentElement.SelectSingleNode((type == x_statusType.output)? SELECT_CDE : SELECT_CODE).InnerText = value; }
		}
		/// <summary>The Root element (ie the element to which additional nodes will be added</summary>
		/// <value>An XmlElement containing the root element</value>
		public XmlElement Root {
			get { return DocumentElement; }
		}
		/// <summary>The Status element (ie the element to which additional nodes will be added</summary>
		/// <value>An XmlElement containing the root element</value>
		public XmlElement Status {
			get { return DocumentElement.SelectSingleNode(SELECT_STATUS) as XmlElement; }
		}
		/// <summary>The Output element (ie the output element in an output type)</summary>
		/// <value>An XmlElement containing the output element</value>
		public XmlElement Output {
			get { return DocumentElement.SelectSingleNode(SELECT_OUTPUT) as XmlElement; }
		}
		#endregion

		
		#region Constructors/Destructors
		/// <overloads>Constructor</overloads>
		/// <summary>Default constructor - assumes the default type</summary>
		public x_status() {
			starttime = DateTime.Now;
		}
		/// <summary>Constructor with time  - assumes the default type</summary>
		/// <param name="start">Start timing mark</param>
		public x_status(DateTime start) {
			starttime = start;
		}
		/// <summary>Result-type constructor</summary>
		/// <param name="ResultName">Start timing mark</param>
		public x_status(string ResultName) {
			starttime = DateTime.Now;
			rootName = ResultName;
			type = x_statusType.result;
		}
		/// <summary>Constructor with the type</summary>
		/// <param name="type">The status type</param>
		public x_status(x_statusType typ) {
			starttime = DateTime.Now;
			type = typ;
		}
		#endregion
		
		#region Public methods
		/// <overloads>Assigns the OK result. (code = "0", text = "OK")</overloads>
		/// <summary>Standard OK result</summary>
		public void AddOk() { 
			initialise();
			Description = DEFAULT_OK;
		}
		/// <summary>OK result with an additional result node</summary>
		/// <param name="nodename">Name of the node</param>
		/// <param name="nodeval">Text value of the node</param>
		public void AddOk(string nodename, string nodeval) {
			AddOk();
			XmlElement newElem = CreateElement(nodename);
       		newElem.InnerText = nodeval;
			DocumentElement.AppendChild(newElem);
		}
		/// <summary>OK result with an additional result node</summary>
		/// <param name="node">Xml node object</param>
		public void AddOk(XmlNode node) {
			initialise(node.OuterXml);
			Description = DEFAULT_OK;
		}
		/// <summary>OK result with an additional result node</summary>
		/// <param name="node">Xml node string</param>
		public void AddOk(string node) {
			initialise(node);
			Description = DEFAULT_OK;
		}
		
		
		/// <overloads>Assigns the error result.</overloads>
		/// <summary>Add default Error result</summary>
		public void AddError() {
			initialise();
			Code = DEFAULT_ERROR_CODE;
			Description = DEFAULT_ERROR;
		}
		/// <summary>Standard error result (text = "notOK")</summary>
		/// <param name="what">Error code</param>
		public void AddError(string what) {
			initialise();
			Code = DEFAULT_ERROR_CODE;
			Description = what;
		}
		/// <summary>Specific error result</summary>
		/// <param name="code">Error code</param>
		/// <param name="description">Error description</param>
		public void AddError(string code, string description) {
			initialise();
			Code = code;
			Description = description;
		}
		/// <summary>Specific error result</summary>
		/// <param name="which">Error description</param>
		/// <param name="code">Error code</param>
		public void AddError(string which, int code) {
			AddError(code.ToString(), which);
		}
		/// <summary>Exception error result</summary>
		/// <param name="except">x_exception error</param>
		public void AddError(x_exception except) {
			AddError(except.Code, except.Message);
			AddNode("Trace", except.StackTrace);
		}
		/// <summary>Exception error result</summary>
		/// <param name="except">Exception error</param>
		public void AddError(Exception except) {
			AddError(except.Message);
			AddNode("Trace", except.StackTrace);
		}
		/// <summary>Exception error result with code</summary>
		/// <param name="except">Exception error</param>
		/// <param name="code">Error code</param>
		public void AddError(Exception except, string code) {
			AddError(code, except.Message);
			AddNode("Trace", except.StackTrace);
		}
		/// <summary>Exception error result with code</summary>
		/// <param name="except">Exception error</param>
		/// <param name="code">Error code</param>
		public void AddError(Exception except, int code) {
			AddError(code.ToString(), except.Message);
		}
		/// <summary>Exception error result</summary>
		/// <param name="except">SecurityException error</param>
		public void AddError(SecurityException except) {
			AddError(String.Concat(except.Message, " -- state:", except.PermissionState));
			AddNode("Trace", except.StackTrace);
		}
		/// <summary>Exception error result</summary>
		/// <param name="except">SqlException error</param>
		public void AddError(SqlException except) {
			AddError(String.Concat(except.Message, " -- server:", except.Server, " -- state:", except.State, " -- source:", except.Source, " -- proc:", except.Procedure, " -- line:", except.LineNumber));
			AddNode("Trace", except.StackTrace);
		}
		/// <summary>Exception error result</summary>
		/// <param name="except">UnauthorizedAccessException error</param>
		public void AddError(UnauthorizedAccessException except) {
			AddError(except.Message);
			AddNode("Trace", except.StackTrace);
		}

		
		/// <overloads>Add nodes to the result xml</overloads>
		/// <summary>Add node as name and text value</summary>
		/// <param name="nodename">Xml Node name</param>
		/// <param name="nodeval">Xml Node value</param>
		public void AddNode(string nodename, string nodeval) {
			XmlElement newElem = this.CreateElement(nodename);
       		newElem.InnerText = nodeval;
			DocumentElement.AppendChild(newElem);
		}
		/// <summary>Node as Xml string</summary>
		/// <param name="nodexml">Xml Node string</param>
		public void AddNode(string nodexml) {
			DocumentElement.InnerXml = String.Concat(DocumentElement.InnerXml, nodexml);
		}
		/// <summary>Node as Xml node</summary>
		/// <param name="thisnode">Xml Node</param>
		public void AddNode(XmlNode thisnode) {
			if (thisnode != null)	// cannot import null node
				DocumentElement.AppendChild(this.ImportNode(thisnode, true));
		}
		/// <summary>Add node with named attribute</summary>
		/// <param name="nodename">Name of the node</param>
		/// <param name="nodeval">Value of the node</param>
		/// <param name="attrname">Name of the attribute</param>
		/// <param name="attrval">Value of the attribute</param>
		public void AddNode(string nodename, string nodeval, string attrname, string attrval) {
			XmlElement newElem = this.CreateElement(nodename);
       		newElem.InnerText = nodeval;
			newElem.SetAttribute(attrname, attrval);
			DocumentElement.AppendChild(newElem);
		}
		
		
		/// <summary>Add multiple nodes to the result xml</summary>
		/// <param name="thisnode">Xml Node containing nodes</param>
		public void AddNodes(XmlNode thisnode) {
			if (thisnode != null)	// cannot import null node
				foreach (XmlNode childnode in thisnode.ChildNodes) {
					XmlNode newnode = this.ImportNode(childnode, true);
					DocumentElement.AppendChild(newnode);
				}
		}
		
		
		/// <overloads>Add elements to the result xml - like AddNode, but returns the element</overloads>
		/// <summary>Add element as name and text value</summary>
		/// <param name="parent">The Xml element's parent (or null)</param>
		/// <param name="elemname">Xml element name</param>
		/// <param name="elemval">Xml element value</param>
		public XmlElement AddElem(XmlElement parent, string elemname, string elemval) {
			if (parent == null)
				parent = DocumentElement;
			XmlElement newElem = this.CreateElement(elemname);
       		newElem.InnerText = elemval;
			return parent.AppendChild(newElem) as XmlElement;
		}
		/// <summary>Add empty element with name</summary>
		/// <param name="elemname">Xml element name</param>
		public XmlElement AddElem(string elemname) {
			return AddElem(null, elemname, "");
		}
		/// <summary>Add empty element with name</summary>
		/// <param name="parent">The Xml element's parent</param>
		/// <param name="elemname">Xml element name</param>
		public XmlElement AddElem(XmlElement parent, string elemname) {
			return AddElem(parent, elemname, "");
		}
		
		
		/// <summary>Adds a CDATA node to the result.</summary>
		/// <param name="name">Name of the node</param>
		/// <param name="text">The text to be added to the node</param>
		public void AddText(string name, string text) {
			XmlElement newElem = this.CreateElement(name);
			XmlCDataSection CData = this.CreateCDataSection(text);    
			newElem.AppendChild(CData);  
			DocumentElement.AppendChild(newElem);
		}

		
		/// <overloads>Set a time value to the result</overloads>
		/// <summary>Time from start to now</summary>
		/// <returns>nothing.</returns>
		public void AddTime() {
			TimeSpan timetaken = DateTime.Now - starttime;
			string thistime = timetaken.TotalSeconds.ToString();
			DocumentElement.SetAttribute("time", thistime);
		}
		/// <summary>Time from start to now with mark</summary>
		/// <param name="mark">description of the mark point</param>
		public void AddTime(string mark) {
			TimeSpan timetaken = DateTime.Now - starttime;
			string thistime = timetaken.TotalSeconds.ToString();
			AddNode(mark, thistime);
		}
		/// <summary>Time from elapsed timespan</summary>
		/// <param name="time">elapsed time value</param>
		public void AddTime(TimeSpan time) {
			string thistime = time.TotalSeconds.ToString();
			DocumentElement.SetAttribute("time", thistime);
		}
		#endregion
		
		#region Protected methods
		protected void _Initialise(XmlNode node) {
			LoadXml(node.OuterXml);
			rootName = DocumentElement.Name;
		}
		protected void _Initialise(XmlElement node) {
			LoadXml(node.OuterXml);
			rootName = DocumentElement.Name;
		}
		protected void _Initialise(string nodexml) {
			LoadXml(nodexml);
			rootName = DocumentElement.Name;
		}
		#endregion
		
		#region Private methods
		/// <overloads>Sets a successful result</overloads>
		/// <summary>Default</summary>
		private void initialise() {
			switch (type) {
				case x_statusType.def:		LoadXml(string.Format(STATUS_XML, rootName));	break;
				case x_statusType.result:	LoadXml(string.Format(RESULT_XML, rootName));	break;
				case x_statusType.output:	LoadXml(string.Format(OUTPUT_XML, "")); break;
				case x_statusType.other:	LoadXml(string.Format(STATUS_XML, rootName)); break;
			}
		}
		private void initialise(string nodexml) {
			switch (type) {
				case x_statusType.def:		LoadXml(string.Format(STATUS_XML, nodexml));				break;
				case x_statusType.result:	LoadXml(string.Format(RESULT_NODEXML, rootName, nodexml));	break;
				case x_statusType.output:	LoadXml(string.Format(OUTPUT_XML, nodexml)); break;
				case x_statusType.other:	LoadXml(string.Format(STATUS_XML, rootName)); break;
			}
		}
		#endregion
	}
}
